from datetime import datetime, timedelta
from typing import Optional, List, Dict, Any, Union

import httpx
import pandas as pd
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from exceptions.exception import ServiceException
from module_kylin.dao.future_trade_cal_dao import FutureTradeCalDao
from module_kylin.entity.future_trade_cal import KylinFutureTradeCal
from module_kylin.entity.vo.future_trade_cal_vo import (
    FutureTradeCalPageQueryModel,
    FutureTradeCalQueryModel,
    DeleteFutureTradeCalModel,
    UpdateFutureTradeCalModel
)
from utils.page_util import PageUtil


class FutureTradeCalService:
    """
    期货交易日历服务层
    """

    @classmethod
    async def get_trade_cal_by_date(cls, db: AsyncSession, cal_date: str) -> Optional[KylinFutureTradeCal]:
        """
        根据日期获取交易日历信息
        
        Args:
            db: 数据库会话
            cal_date: 日历日期（格式：YYYYMMDD）
            
        Returns:
            KylinFutureTradeCal对象或None
        """
        return await FutureTradeCalDao.get_trade_cal_by_date(db, cal_date)

    @classmethod
    async def get_trade_cal_list_services(
        cls,
        db: AsyncSession,
        query_model: Union[FutureTradeCalPageQueryModel, FutureTradeCalQueryModel],
        is_page: bool = False
    ) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
        """
        获取期货交易日历列表
        
        Args:
            db: 数据库会话
            query_model: 查询模型
            is_page: 是否分页
            
        Returns:
            分页数据或列表数据
        """
        # 获取交易日历列表
        trade_cal_list = await FutureTradeCalDao.get_trade_cal_list(db, query_model)
        
        # 转换为字典列表
        result_list = []
        for item in trade_cal_list:
            item_dict = {
                'id': item.id,
                'calDate': item.cal_date,
                'isOpen': item.is_open,
                'pretradeDate': item.pretrade_date,
                'createTime': item.create_time.strftime('%Y-%m-%d %H:%M:%S') if item.create_time else None,
                'updateTime': item.update_time.strftime('%Y-%m-%d %H:%M:%S') if item.update_time else None
            }
            result_list.append(item_dict)
        
        # 分页处理
        if is_page and hasattr(query_model, 'page_num') and hasattr(query_model, 'page_size'):
            return PageUtil.get_page_obj(result_list, query_model.page_num, query_model.page_size)
        
        return result_list
    
    @classmethod
    async def add_trade_cal_services(
        cls,
        db: AsyncSession,
        trade_cal_data: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        添加期货交易日历
        
        Args:
            db: 数据库会话
            trade_cal_data: 交易日历数据
            
        Returns:
            操作结果
        """
        # 检查日期是否已存在
        existing_cal = await FutureTradeCalDao.get_trade_cal_by_date(db, trade_cal_data.get('cal_date'))
        if existing_cal:
            # 日期已存在，返回提示信息而不抛出异常
            return {'success': False, 'msg': f'日期 {trade_cal_data.get("cal_date")} 已存在'}
        
        # 创建交易日历对象（直接使用传入的pretrade_date，不再自动计算）
        trade_cal = KylinFutureTradeCal(
            cal_date=trade_cal_data.get('cal_date'),
            is_open=bool(trade_cal_data.get('is_open', 1)),  # 转换为布尔类型
            pretrade_date=trade_cal_data.get('pretrade_date')  # 支持手动设置上一个交易日
        )
        
        # 保存到数据库
        await FutureTradeCalDao.add_trade_cal(db, trade_cal)
        # 提交事务
        await db.commit()
        
        return {'success': True, 'message': '添加成功'}
    
    @classmethod
    async def update_trade_cal_services(
        cls,
        db: AsyncSession,
        update_data: UpdateFutureTradeCalModel
    ) -> Dict[str, Any]:
        """
        更新期货交易日历
        
        Args:
            db: 数据库会话
            update_data: 更新数据
            
        Returns:
            操作结果
        """
        # 更新交易日历
        update_dict = {
            'is_open': update_data.is_open,
            'update_time': datetime.now()
        }
        
        # 如果提供了上一个交易日，则更新
        if hasattr(update_data, 'pretrade_date') and update_data.pretrade_date is not None:
            update_dict['pretrade_date'] = update_data.pretrade_date
        
        result = await FutureTradeCalDao.update_trade_cal(db, update_data.id, update_dict)
        
        if result == 0:
            raise ServiceException(message='交易日历不存在')
        
        return {'success': True, 'message': '更新成功'}
    
    @classmethod
    async def delete_trade_cal_services(
        cls,
        db: AsyncSession,
        delete_data: DeleteFutureTradeCalModel
    ) -> Dict[str, Any]:
        """
        删除期货交易日历
        
        Args:
            db: 数据库会话
            delete_data: 删除数据
            
        Returns:
            操作结果
        """
        # 删除交易日历
        result = await FutureTradeCalDao.delete_trade_cal(db, delete_data.cal_ids)
        
        return {'success': True, 'message': f'删除成功，共删除 {result} 条记录'}
    
    @classmethod
    async def sync_tushare_data_services(
        cls,
        db: AsyncSession
    ) -> Dict[str, Any]:
        """
        从Tushare同步交易日历数据
        
        Args:
            db: 数据库会话
            
        Returns:
            同步结果
        """
        # 获取本地最大日期
        max_cal_date = await FutureTradeCalDao.get_max_cal_date(db)
        
        # 确定同步起始日期
        if max_cal_date:
            start_date = (datetime.strptime(max_cal_date, '%Y%m%d') + timedelta(days=1)).strftime('%Y%m%d')
        else:
            # 如果没有数据，默认从2020年开始同步
            start_date = '20200101'
        
        # 确定同步结束日期（当前日期加1年）
        end_date = (datetime.now() + timedelta(days=365)).strftime('%Y%m%d')
        
        # 如果起始日期已超过结束日期，无需同步
        if start_date > end_date:
            return {'success': True, 'message': '当前数据已是最新', 'synced_count': 0}
        
        try:
            # 调用Tushare API获取数据
            trade_cal_data = await cls._fetch_tushare_trade_cal(start_date, end_date)
            
            if trade_cal_data.empty:
                return {'success': True, 'message': '未获取到新数据', 'synced_count': 0}
            
            # 批量添加到数据库
            trade_cal_list = []
            for _, row in trade_cal_data.iterrows():
                # 检查是否已存在
                existing = await FutureTradeCalDao.get_trade_cal_by_date(db, row['cal_date'])
                if not existing:
                    trade_cal = KylinFutureTradeCal(
                        cal_date=row['cal_date'],
                        is_open=row['is_open'],
                        pretrade_date=row.get('pretrade_date')  # 确保正确保存上一个交易日
                    )
                    trade_cal_list.append(trade_cal)
            
            # 批量保存
            if trade_cal_list:
                await FutureTradeCalDao.batch_add_trade_cal(db, trade_cal_list)
            
            return {
                'success': True, 
                'message': f'同步成功', 
                'synced_count': len(trade_cal_list),
                'start_date': start_date,
                'end_date': end_date
            }
            
        except Exception as e:
            raise ServiceException(message=f'同步失败: {str(e)}')
    
    @classmethod
    async def _fetch_tushare_trade_cal(cls, start_date: str, end_date: str) -> Optional[pd.DataFrame]:
        """
        从Tushare获取交易日历数据
        
        Args:
            start_date: 开始日期
            end_date: 结束日期
            
        Returns:
            交易日历数据DataFrame
        """
        # 从配置文件获取Tushare API的token
        from config.env import get_config
        from config.kylin_config import KylinDev, KylinPrd
        
        # 获取应用配置
        app_config = get_config.get_app_config()
        
        # 根据环境选择对应的配置
        if app_config.app_env == 'prod':
            token = KylinPrd.TUSHARE_TOKEN
        else:
            token = KylinDev.TUSHARE_TOKEN
        
        # Tushare API接口
        url = 'http://api.tushare.pro'
        headers = {'Content-Type': 'application/json'}
        
        # 构建请求参数，设置is_open=1获取所有交易日数据，包括pretrade_date
        params = {
            'api_name': 'trade_cal',
            'token': token,
            'params': {
                'exchange': 'SHFE',  # 上期所
                'start_date': start_date,
                'end_date': end_date,
                'is_open': ''  # 获取所有日期数据，包括交易日和非交易日
            }
        }
        
        # 发送请求
        async with httpx.AsyncClient() as client:
            response = await client.post(url, headers=headers, json=params)
            
            # 检查响应
            if response.status_code == 200:
                data = response.json()
                if data.get('code') == 0 and data.get('data'):
                    # 转换为DataFrame
                    df = pd.DataFrame(data['data']['items'], columns=data['data']['fields'])
                    
                    # 过滤只保留SHFE交易所的数据
                    df = df[df['exchange'] == 'SHFE']
                    
                    # 返回所有可能的字段，包括pretrade_date（如果API提供）
                # 但至少保留cal_date和is_open
                if 'pretrade_date' in df.columns:
                    return df[['cal_date', 'is_open', 'pretrade_date']]
                else:
                    # 如果API没有提供pretrade_date字段，保持为空
                    return df[['cal_date', 'is_open']]
        
        return None